//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================

#include <errno.h>
#include <ctype.h>
#include <math.h>
#include <float.h>
#include <stdlib.h>
#include <string.h>
#include <malloc.h>
#include <elatypes.h>
#include <limits.h>
#include <_pubcrt.h>

/*The IEEE double precision floating point standard representation requires
 *a 64
 *bit word, which may be represented as numbered from 0 to 63, left to right.
 *The first bit is the sign bit, S, the next eleven bits are the exponent
 * bits, 'E',and the final 52 bits are the fraction 'F':
S EEEEEEEEEEE FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
0 1        11 12                                                63           */

#ifdef _MSC_VER
#pragma optimize( "", off )
#endif

double strtod(const char *str, char **endptr)
{
    register const char*  p = str;
    const char *str0;
    UInt64 temp_frac, bina_frac, hi_frac = 0, lo_frac = 0;
    UInt64 hi, lo, bina_value = 0;
    int temp_exp, dec_exp = 0, bina_exp = 0, e_exp = 0;
    int i, sign = 1, e_sign = 1;
    fd_twoints u;

    static UInt64 decpow[] =
    {ULLONG(1),                         ULLONG(10),                          
     ULLONG(100),                       ULLONG(1000),                    
     ULLONG(10000),                     ULLONG(100000),
     ULLONG(1000000),                   ULLONG(10000000),                  
     ULLONG(100000000),                 ULLONG(1000000000),              
     ULLONG(10000000000),               ULLONG(100000000000),
     ULLONG(1000000000000),             ULLONG(10000000000000),        
     ULLONG(100000000000000),           ULLONG(1000000000000000),        
     ULLONG(10000000000000000),         ULLONG(100000000000000000),
     ULLONG(1000000000000000000),       ULLONG(10000000000000000000)
    };

    while (isspace(*p)) p++;

    str = p;
    switch (*p) {
        case '-': sign = -1;
        case '+': p++;
        default : break;
    }

    str0 = p;
    while ('0' == *p) p++;
    for (i = 18; (unsigned int)(*p - '0') < 10u; i--, p++) {
        if (i >= 0) hi_frac += (*p - '0') * decpow[i];
    }
    dec_exp -= (i+1);

    if (*p == '.') {
        p++;
        if (0 == hi_frac) {
            for (;'0' == *p; p++) dec_exp--;
        }
        for (; (unsigned int)(*p - '0') < 10u; i--, p++) {
            if (i >= 0) hi_frac += (*p - '0') * decpow[i];
        }
    }

    if ((p == str0) || ((*(p-1) == '.') && ((p-1) == str0))) {
        p = str;
    }
    else if ((*p | 32) == 'e' || (*p | 32) == 'd') {
        str0 = p;

        switch (*++p) {
            case '-': e_sign = -1;
            case '+':
                p++;
                break;
        }

        if ((unsigned int)(*p - '0') < 10u) {
            while ((unsigned int)(*p - '0') < 10u) {
                e_exp = 10 * e_exp + (*p++ - '0');
            }
            dec_exp += (e_exp * e_sign);
        }
        else {
            p = str0;
        }
    }
    /* The program above is to put decimal fraction with nighteen places in
    hi_frac variable and decimal exponent in dec_exp variable;
       The program below is to convert decimal fraction to binary fraction, and
    still in hi_frac variable, and decimal exponent to binary exponent, and put
    it in bina_exp variable, which is programmed by shifting bits and
    increasing
    or decreasing tenfold.*/

    if (hi_frac) {
        if (dec_exp >= 0) {
            while (dec_exp > 0) {
                lo_frac >>= 10;
                temp_frac = hi_frac << 54;
                if (temp_frac) {
                    temp_frac >>= 10;
                    lo_frac += temp_frac;
                }
                hi_frac >>= 10;
                bina_exp += 10;
                while (!(hi_frac >> 54) && dec_exp > 0) {
                    temp_exp = dec_exp > 2 ? 3 : dec_exp;
                    hi_frac *= decpow[temp_exp];
                    hi_frac += (lo_frac *= decpow[temp_exp]) >> 54;
                    lo_frac &= ((ULLONG(1) << 54) - 1);
                    dec_exp -= 3;
                }
            }
        }
        else {
            while (dec_exp < 0) {
                while ((hi_frac >> 54) && dec_exp < 0) {
                    temp_exp = dec_exp < -2 ? 3 : -dec_exp;
                    lo_frac += (hi_frac % decpow[temp_exp]) << 54;
                    lo_frac /= decpow[temp_exp];
                    hi_frac /= decpow[temp_exp];
                    dec_exp += 3;
                }
                if (dec_exp < 0) {
                    hi_frac <<= 10;
                    lo_frac <<= 10;
                    if (lo_frac >> 54) {
                        hi_frac += lo_frac >> 54;
                        lo_frac &= ((ULLONG(1) << 54) - 1);
                    }
                    bina_exp -= 10;
                }
            }
        }
        for (i = 63; i > 0; i--) {
            if (hi_frac >> i) {
                hi_frac <<= (63 - i);
                hi_frac += lo_frac >> (i - 9);
                bina_exp -= (63 - i);
                break;
            }
        }
        hi_frac >>= 1;
        hi_frac += (ULLONG(1) << 9);
        if (hi_frac >> 63) {
            bina_exp += 64;
        }
        else {
            hi_frac <<= 1;
            bina_exp += 63;
        }

        if (bina_exp > 1023) {
            bina_value = DOUBLE_EXP_MASK;
        }
        else if (bina_exp >= -1022) {
            bina_frac = (hi_frac >> 11) & DOUBLE_FRACTION_MASK;
            bina_value = ((UInt64)(bina_exp + 1023) << 53 >> 1) | bina_frac;
        }
        else if (bina_exp >= (-1022 - 52)) {
            bina_value =
                (hi_frac >> 11 >> (- bina_exp - 1022)) & DOUBLE_FRACTION_MASK;
        }
    }

    if (-1 == sign) bina_value |= (ULLONG(1) << 63);
    hi = (bina_value & DOUBLE_HI_MASK) >> 32;
    lo = bina_value & DOUBLE_LO_MASK;
    __HI(u) = *((UInt32 *)&hi);
    __LO(u) = *((UInt32 *)&lo);

    if (endptr != NULL) *endptr = (char*)p;

    return u.d;
}

#ifdef _MSC_VER
#pragma optimize( "", on )
#endif

double __cdecl wcstod(const wchar_t *nptr, wchar_t **endptr)
{
    double r;   /* result */
    char *buf, *end;
    size_t len;

    r = 0.0;

    while ((*nptr == ' ') || (*nptr == '\t')) {
        nptr++;
    }
    len = wcslen(nptr);
    buf = (char *)_alloca(len + 1);
    if (buf == NULL) return (0.0);

    _wcstostr(buf, nptr);
    r = strtod(buf, &end);
    if (endptr != NULL) {
        *endptr = (wchar_t *)nptr + (end - buf);
    }
    return r;
}

double atof(const char *str)
{
    return strtod(str, NULL);
}
